package spindles.api.services;

import static spindles.api.util.MathUtil.transpose;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import spindles.api.domain.CutoffFrequency;
import spindles.api.domain.SamplingRate;
import spindles.api.util.ApplicationException;
import spindles.api.util.Config;
import spindles.matlab.Matlab;

import com.mathworks.toolbox.javabuilder.MWArray;
import com.mathworks.toolbox.javabuilder.MWCellArray;
import com.mathworks.toolbox.javabuilder.MWClassID;
import com.mathworks.toolbox.javabuilder.MWException;
import com.mathworks.toolbox.javabuilder.MWJavaObjectRef;
import com.mathworks.toolbox.javabuilder.MWLogicalArray;
import com.mathworks.toolbox.javabuilder.MWNumericArray;
import com.mathworks.toolbox.javabuilder.webfigures.WebFigure;

public class MatlabGateway {
	final Logger log = LoggerFactory.getLogger(MatlabGateway.class);
	
	private Matlab matlab;

	private static MatlabGateway instance = new MatlabGateway();

	protected MatlabGateway() {
		try {
			matlab = new Matlab();
		} catch (MWException e) {
			throw new ApplicationException(e);
		}
	}

//	public static void init() {
//		instance = new MatlabGateway();
//	}
	
	public static WebFigure plotSpindlesDensityWeb(int intervalLength,
			double[] spindlesOccurrences, String plotTitle, String titleText,
			int plotWidth, int plotHeight) {

		Object[] outputArray = null;
		WebFigure result = null;

		try {
			outputArray = instance.matlab.plotSpindlesDensityWeb(1,
					(double) intervalLength, spindlesOccurrences, plotTitle,
					titleText, (double) plotWidth, (double) plotHeight);
			result = (WebFigure) MWJavaObjectRef
					.unwrapJavaObjectRefs(outputArray[0]);
			return result;
		} catch (MWException e) {
			throw new ApplicationException(e);
		} finally {
			MWArray.disposeArray(outputArray);
		}
	}
	
	public static WebFigure plotEEGWeb(double data[][], 
			int samplingFrequency,
			Object[] spindles, 
			String[] plotTitles, 
			String titleText,
			int plotWidth,
			int plotHeight,
			boolean[] filtered		
			){
		
		MWCellArray spindlesCell = null;
		MWCellArray titlesCell = null;		
		MWLogicalArray filteredArray = null;
		MWNumericArray array = null;
		
		Object[] outputArray = null;
		WebFigure result = null;
		try {
			data = transpose(data);
			spindlesCell = toCell(spindles.length);
			
			for(int i = 0; i < spindles.length; i++){
				array = new MWNumericArray(((double[][])spindles[i]), 
						MWClassID.DOUBLE);
				instance.log.info("Spindle data: "  + array.toString());
				if(!array.toString().equals("[]")){
					spindlesCell.set(i + 1, array);
				}				
			}
			
			titlesCell = toCell(plotTitles.length);
			
			for(int i = 0; i < plotTitles.length; i++){
				titlesCell.set(i + 1, plotTitles[i]);
			}
			
			filteredArray = new MWLogicalArray(filtered);
			
			instance.log.info("Should export {} spindles", spindlesCell.numberOfElements());
			outputArray = instance.matlab.plotEEGWeb(1, data,
					(double) samplingFrequency, spindlesCell, titlesCell,
					titleText, (double) plotWidth, (double) plotHeight,
					filteredArray);
			result = (WebFigure) MWJavaObjectRef.unwrapJavaObjectRefs(outputArray[0]);			
			
			return result;
		} catch (MWException e) {
			throw new ApplicationException(e);
		} finally{
			MWArray.disposeArray(outputArray);
			dispose(spindlesCell);
			dispose(titlesCell);
			dispose(filteredArray);
			dispose(array);			
		}		
	}
	
	public static void plotEEG(double data[][], 
			int samplingFrequency,
			Object[] spindles, 
			String[] plotTitles, 
			String titleText,
			String[] formats, 
			String fileName, 
			boolean append,
			boolean[] filtered) {
		
		MWCellArray spindlesCell = null;
		MWCellArray titlesCell = null;
		MWCellArray formatsCell = null;
		MWLogicalArray filteredArray = null;
		MWNumericArray array = null;
		
		try {
			data = transpose(data);			
			
			spindlesCell = toCell(spindles.length);
			
			for(int i = 0; i < spindles.length; i++){
				array = new MWNumericArray(((double[][])spindles[i]), 
						MWClassID.DOUBLE);
				if(!array.toString().equals("[]")){
					spindlesCell.set(i + 1, array);
				}				
			}		
			
			titlesCell = toCell(plotTitles.length);
			
			for(int i = 0; i < plotTitles.length; i++){
				titlesCell.set(i + 1, plotTitles[i]);
			}
			
			formatsCell = toCell(formats.length);
			
			for(int i = 0; i < formats.length; i++){
				formatsCell.set(i + 1, formats[i]);
			}
			
			filteredArray = new MWLogicalArray(filtered);
			
			instance.matlab.plotEEG(data, (double)samplingFrequency, 
					spindlesCell, titlesCell, titleText, formatsCell, 
					fileName, append, filteredArray);
			
		} catch (MWException e) {
			throw new ApplicationException(e);
		}
		finally{
			dispose(spindlesCell);
			dispose(titlesCell);
			dispose(formatsCell);
			dispose(filteredArray);
			dispose(array);
		}		
	}
	
	private static MWCellArray toCell(int length){
		int[] cdims = {1, length};
		return new MWCellArray(cdims);
	}
	

	public static double[] filterEEG(double[] data, CutoffFrequency cutOffFreq) {
		
		Object[] result = null;

		try {
			result = instance.matlab.filterEEG(1, data, 
					cutOffFreq.getValue());

			return toDouble(result);

		} catch (MWException e) {
			throw new ApplicationException(e);
		} finally {
			MWArray.disposeArray(result);
		}
	}
	
	public static double[] eegPeaks(double data[], SamplingRate sr){
		Object[] result = null;

		try {
			result = instance.matlab.eegPeaks(1, 
								data, 
								CutoffFrequency.getCutoffFreq(sr).getValue(), 
								sr.value(),
								Config.getBptrPath());

			return toDouble(result);

		} catch (MWException e) {
			throw new ApplicationException(e);
		} finally {
			MWArray.disposeArray(result);
		}	
	}
	
	public static void writeToMAT(double[] data, String fileName){
		try {			
			instance.matlab.writeToMAT(data, fileName);		

		} catch (MWException e) {
			throw new ApplicationException(e);
		} 
	}

	public static void dispose() {
		if (instance.matlab != null) {
			instance.matlab.dispose();
			instance.matlab = null;
		}
	}
	
	public static void dispose(MWArray m){
		if(m != null){
			m.dispose();
		}
	}

	private static double[] toDouble(Object[] result) {
		return (double[]) ((MWArray) result[0]).getData();
	}

}
